/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
* C++ interface for pnglite library.
* Can be used to encode and decode png images 
* 
* Required libraries:
* - zlib and pnglite
* - for debug SDL and SDL gfx
* 
* To compile debug:
* g++ -o png-test -DPNG_LITE_INTERFACE_DEBUG PNGLiteInterface.cpp -lpnglite -lz -lSDL -lSDL_gfx
*/

#include <stdio.h>
#include <stdlib.h>
#include <pnglite.h>
#include <string.h>
#ifdef PNG_LITE_INTERFACE_DEBUG
#include <SDL/SDL.h>
#include <SDL/SDL_gfxPrimitives.h>
#include <unistd.h>
#endif

class CPNGLiteInterface{
	public:
		CPNGLiteInterface();
		~CPNGLiteInterface();
		
		/**
		* Decodes a png data from memory
		* @param *png_img Pointer to a valid png data
		* @return true is decoding was success
		*/
		bool decode(const unsigned char *png_img);
		
		/**
		* Decodes a png data from file
		* @param *pngfile filename
		* @return true is decoding was success
		*/
		bool decodeFromFile(const char *pngfile);

		/**
		* Encode from memory 
		* @param *data The image data to be compressed
		* @param int w width of the image
		* @param int h height of the data
		* @param color mode of the data. Defined by pnglite.h in a following manner:
		*		enum
		*		{
		*			PNG_GREYSCALE			= 0,
		*			PNG_TRUECOLOR			= 2,
		*			PNG_INDEXED				= 3,
		*			PNG_GREYSCALE_ALPHA		= 4,
		*			PNG_TRUECOLOR_ALPHA		= 6
		*		};
		* This interface supports at least the two first ones (others are sophisticated guesses)
		* @return true on success
		*/
		bool encode(unsigned char *data, int w, int h, int color);
		
		/**
		* Get pointer to encoded/decoded image
		* @return pointer to image data, NULL if not valid
		*/
		unsigned char * getImagePtr();
		
		/**
		* Get copy of the image data 
		* @param *data the pointer where the data is copied to. The memory must be allocated properly 
		* @return true, if success
 		*/
		bool getImageCopy(unsigned char *data);
		
		/**
		* Saves an image to a file (should be used only with compressed png)
		* @param *fname the name of file
		* @return true if success
		*/
		bool save(const char *fname);
		
		/**
		* Get the size of the image in bytes
		* @return size of image, 0 if the image is not valid 
		*/
		int size();
		/**
		* Get the width of the image in pixels
		* @return width of image, 0 if the image is not valid 
		*/
		int width();
		
		/**
		* Get the height of the image in pixels
		* @return height of image, 0 if the image is not valid 
		*/
		int height();
		
		/**
		* Get type (color type) of the image
		* @return type (as defined by the pnglite.h), negative if image is not valid
		*/
		int colorType();
		
		/**
		* Get bytes per pixel
		* @return bytes per pixel (0 if not valid)
		*/
		int bpp();
		
		/**
		* Print info
		*/
		void print(){png_print_info(&png);}
		
	private:
		unsigned char *img;     ///< Image data is stored here 
		unsigned int img_size;  ///< This is how much memory is allocated for the *img
		png_t png;							///< png lite structure
		unsigned int  data_size; ///< Size of the data used in by callbacks.
		bool isValid;						///< is everything ok
		const unsigned char *ptrToPng;
		
		/**
		* Writes a raw image data (input) to PNG (user_pointer)
		* used by: png_open_write(&png, png_write_callback, ptr);
		*/
		 static unsigned png_write_callback(void* input, size_t size, size_t numel, void* user_pointer){
			CPNGLiteInterface *pngptr = (CPNGLiteInterface *) user_pointer;
			
			memcpy( ((unsigned char *)((pngptr->img)+pngptr->data_size)), input, numel*size);
			pngptr->data_size += numel*size;
			return (numel*size);
		}
		
		/**
		* Reads image data from user_pointer and converts it into PNG
		* Used by: png_open_read(&png, png_read_callback, buffer);
		* TODO: How to prevent corrupted data to cause segmentation fault??
		*/
		static unsigned png_read_callback(void* output, size_t size, size_t numel, void* user_pointer){
			CPNGLiteInterface *pngptr = (CPNGLiteInterface *) user_pointer;
			//fprintf(stderr,"rcb: %p %p %d,%d data=%d\n",pngptr->ptrToPng, user_pointer,size,numel,pngptr->data_size);
			
			/**
			* If there is memory reserved for image and the size would be larger then set the input to NULL
			* The encoder will stop to encode in few iterations...
			*/
			if(pngptr->data_size+(numel*size)>pngptr->img_size && pngptr->img_size>0 || pngptr->ptrToPng==NULL){
				pngptr->ptrToPng=NULL;
				return 0;
			}
			
			if(output == NULL) {
				pngptr->data_size+=(numel*size);
				return 0;
			}
			memcpy(output, (pngptr->ptrToPng+pngptr->data_size), numel*size);
			pngptr->data_size += numel*size;
			return (numel*size);
		}

		void checkMemory(unsigned int size); ///< reserves memory for *img as required
};
